/*
    RateIt
    version 1.0.4
    03/31/2012
    http://rateit.codeplex.com
    Twitter: @gjunge

*/
(function ($) {
  $.fn.rateit = function (p1, p2) {
    //quick way out.
    var options = {}; var mode = 'init';
    var capitaliseFirstLetter = function (string) {
      return string.charAt(0).toUpperCase() + string.substr(1);
    };

    if (this.length == 0) return this;


    var tp1 = $.type(p1);
    if (tp1 == 'object' || p1 === undefined || p1 == null) {
      options = $.extend({}, $.fn.rateit.defaults, p1); //wants to init new rateit plugin(s).
    }
    else if (tp1 == 'string' && p2 === undefined) {
      return this.data('rateit' + capitaliseFirstLetter(p1)); //wants to get a value.
    }
    else if (tp1 == 'string') {
      mode = 'setvalue'
    }

    return this.each(function () {
      var item = $(this);

      //shorten all the item.data('rateit-XXX'), will save space in closure compiler, will be like item.data('XXX') will become x('XXX')
      var itemdata = function (key, value) {
          arguments[0] = 'rateit' + capitaliseFirstLetter(key); 
          return item.data.apply(item, arguments); ////Fix for WI: 523
      };

      //add the rate it class.
      if (!item.hasClass('rateit')) item.addClass('rateit');

      var ltr = item.css('direction') != 'rtl';

      // set value mode
      if (mode == 'setvalue') {
        if (!itemdata('init')) throw 'Can\'t set value before init';


        //if readonly now and it wasn't readonly, remove the eventhandlers.
        if (p1 == 'readonly' && !itemdata('readonly')) {
          item.find('.rateit-range').unbind();
          itemdata('wired', false);
        }

        if (itemdata('backingfld')) {
          //if we have a backing field, check which fields we should update. 
          //In case of input[type=range], although we did read its attributes even in browsers that don't support it (using fld.attr())
          //we only update it in browser that support it (&& fld[0].min only works in supporting browsers), not only does it save us from checking if it is range input type, it also is unnecessary.
          var fld = $(itemdata('backingfld'));
          if (p1 == 'value') fld.val(p2);
          if (p1 == 'min' && fld[0].min) fld[0].min = p2;
          if (p1 == 'max' && fld[0].max) fld[0].max = p2;
          if (p1 == 'step' && fld[0].step) fld[0].step = p2;
        }

        itemdata(p1, p2);
      }

      //init rateit plugin
      if (!itemdata('init')) {

        //get our values, either from the data-* html5 attribute or from the options.
        itemdata('min', itemdata('min') || options.min);
        itemdata('max', itemdata('max') || options.max);
        itemdata('step', itemdata('step') || options.step);
        itemdata('readonly', itemdata('readonly') !== undefined ? itemdata('readonly') : options.readonly);
        itemdata('resetable', itemdata('resetable') !== undefined ? itemdata('resetable') : options.resetable);
        itemdata('backingfld', itemdata('backingfld') || options.backingfld);
        itemdata('starwidth', itemdata('starwidth') || options.starwidth);
        itemdata('starheight', itemdata('starheight') || options.starheight);
        itemdata('value', itemdata('value') || options.min);
        itemdata('ispreset', itemdata('ispreset') !== undefined ? itemdata('ispreset') : options.ispreset);
        //are we LTR or RTL?

        if (itemdata('backingfld')) {
          //if we have a backing field, hide it, and get its value, and override defaults if range.
          var fld = $(itemdata('backingfld'));
          itemdata('value', fld.hide().val());

          if (fld[0].nodeName == 'INPUT') {
            if (fld[0].type == 'range' || fld[0].type == 'text') { //in browsers not support the range type, it defaults to text

              itemdata('min', parseInt(fld.attr('min')) || itemdata('min')); //if we would have done fld[0].min it wouldn't have worked in browsers not supporting the range type.
              itemdata('max', parseInt(fld.attr('max')) || itemdata('max'));
              itemdata('step', parseInt(fld.attr('step')) || itemdata('step'));
            }
          }
          if (fld[0].nodeName == 'SELECT' && fld[0].options.length > 1) {
            itemdata('min', Number(fld[0].options[0].value));
            itemdata('max', Number(fld[0].options[fld[0].length - 1].value));
            itemdata('step', Number(fld[0].options[1].value) - Number(fld[0].options[0].value));
          }
        }

        //Create the necessary tags.
        item.append('<div class="rateit-reset"></div><div class="rateit-range"><div class="rateit-selected" style="height:' + itemdata('starheight') + 'px"></div><div class="rateit-hover" style="height:' + itemdata('starheight') + 'px"></div></div>');

        //if we are in RTL mode, we have to change the float of the "reset button"
        if (!ltr) {
          item.find('.rateit-reset').css('float', 'right');
          item.find('.rateit-selected').addClass('rateit-selected-rtl');
          item.find('.rateit-hover').addClass('rateit-hover-rtl');
        }
        itemdata('init', true);
      }


      //set the range element to fit all the stars.
      var range = item.find('.rateit-range');
      range.width(itemdata('starwidth') * (itemdata('max') - itemdata('min'))).height(itemdata('starheight'));

      //add/remove the preset class
      var presetclass = 'rateit-preset' + ((ltr) ? '' : '-rtl');
      if (itemdata('ispreset'))
        item.find('.rateit-selected').addClass(presetclass);
      else
        item.find('.rateit-selected').removeClass(presetclass);

      //set the value if we have it.
      if (itemdata('value')) {
        var score = (itemdata('value') - itemdata('min')) * itemdata('starwidth');
        item.find('.rateit-selected').width(score);
      }

      var resetbtn = item.find('.rateit-reset');

      var calcRawScore = function (element, event) {
        var pageX = (event.changedTouches) ? event.changedTouches[0].pageX : event.pageX;

        var offsetx = pageX - $(element).offset().left;
        if (!ltr) offsetx = range.width() - offsetx;
        if (offsetx > range.width()) offsetx = range.width();
        if (offsetx < 0) offsetx = 0;

        return score = Math.ceil(offsetx / itemdata('starwidth') * (1 / itemdata('step')));
      };


      //

      if (!itemdata('readonly')) {
        //if we are not read only, add all the events

        //if we have a reset button, set the event handler.
        if (itemdata('resetable')) {
          resetbtn.click(function () {
            itemdata('value', itemdata('min'));
            range.find('.rateit-hover').hide().width(0);
            range.find('.rateit-selected').width(0).show();
            if (itemdata('backingfld')) $(itemdata('backingfld')).val(itemdata('min'));
            item.trigger('reset');
          });

        }
        else {
          resetbtn.hide();
        }



        //when the mouse goes over the range div, we set the "hover" stars.
        if (!itemdata('wired')) {
          range.bind('touchmove touchend', touchHandler); //bind touch events
          range.mousemove(function (e) {
            var score = calcRawScore(this, e);
            var w = score * itemdata('starwidth') * itemdata('step');
            var h = range.find('.rateit-hover');
            if (h.data('width') != w) {
              range.find('.rateit-selected').hide();
              h.width(w).show().data('width', w);
              var data = [(score * itemdata('step')) + itemdata('min')];
              item.trigger('hover', data).trigger('over', data);
            }
          });
          //when the mouse leaves the range, we have to hide the hover stars, and show the current value.
          range.mouseleave(function (e) {
            range.find('.rateit-hover').hide().width(0).data('width', '');
            item.trigger('hover', [null]).trigger('over', [null]);
            range.find('.rateit-selected').show();
          });
          //when we click on the range, we have to set the value, hide the hover.
          range.mouseup(function (e) {
            var score = calcRawScore(this, e);

            var newvalue = (score * itemdata('step')) + itemdata('min');
            itemdata('value', newvalue);
            if (itemdata('backingfld')) {
              $(itemdata('backingfld')).val(newvalue);
            }
            if (itemdata('ispreset')) { //if it was a preset value, unset that.
              range.find('.rateit-selected').removeClass(presetclass);
              itemdata('ispreset', false);
            }
            range.find('.rateit-hover').hide();
            range.find('.rateit-selected').width(score * itemdata('starwidth') * itemdata('step')).show();
            item.trigger('hover', [null]).trigger('over', [null]).trigger('rated', [newvalue]);
          });

          itemdata('wired', true);
        }
        if (itemdata('resetable')) {
          resetbtn.show();
        }
      }
      else {
        resetbtn.hide();
      }
    });
  };

  //touch converter http://ross.posterous.com/2008/08/19/iphone-touch-events-in-javascript/
  function touchHandler(event) {

    var touches = event.originalEvent.changedTouches,
                first = touches[0],
                type = "";
    switch (event.type) {
      case "touchmove": type = "mousemove"; break;
      case "touchend": type = "mouseup"; break;
      default: return;
    }

    var simulatedEvent = document.createEvent("MouseEvent");
    simulatedEvent.initMouseEvent(type, true, true, window, 1,
                              first.screenX, first.screenY,
                              first.clientX, first.clientY, false,
                              false, false, false, 0/*left*/, null);

    first.target.dispatchEvent(simulatedEvent);
    event.preventDefault();
  };

  //some default values.
  $.fn.rateit.defaults = { min: 0, max: 5, step: 0.5, starwidth: 16, starheight: 16, readonly: false, resetable: true, ispreset: false };

  //invoke it on all div.rateit elements. This could be removed if not wanted.
  $(function () { $('div.rateit').rateit(); });

})(jQuery);